//将color的16进制数值转成rgb
function getRgb(color) {
    color = color.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, function (color, t, a, i) {
        return t + t + a + a + i + i;
    });
    var rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
    return rgb ? {
        r: parseInt(rgb[1], 16),
        g: parseInt(rgb[2], 16),
        b: parseInt(rgb[3], 16)
    } : null;
}
//对resize函数进行节流
function throttle(fn, delay) {
    var timer = null;
    return function () {
        clearInterval(timer);
        timer = setTimeout(fn, delay);
    }
}

var defaultOption = {
    minSpeedX: 0.1,
    maxSpeedX: 0.7,
    minSpeedY: 0.1,
    maxSpeedY: 0.7,
    directionX: 'center', // 'center', 'left' or 'right'. 'center' = dots bounce off edges
    directionY: 'center', // 'center', 'up' or 'down'. 'center' = dots bounce off edges
    density: 20000, // How many particles will be generated: one particle every n pixels
    dotColor: '#79f1a4',
    lineColor: '#79f1a4',
    dotOpacity: 1,
    dotMinSize: 3,
    dotMaxSize: 20,
    dotShape: "circle",
    sideNumber: 5,
    lineOpacity: 1,
    lineWidth: 1,
    curvedLines: false,
    clickAddDot: false,
    addDotNumber: 1,
    proximity: 100, // How close two dots need to be before they join
    parallax: true, //视差
    parallaxMultiplier: 5, // The lower the number, the more extreme the parallax effect
    onInit: function () {
    },
    onDestroy: function () {
    }
};

function ParticleGround(el,option = defaultOption){
        var canvas;
        var ctx;
        var winW;
        var winH;
        var canvasSupport = !!document.createElement('canvas').getContext;
        var particles = [];
        var desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i);
        var orientationSupport = !!window.DeviceOrientationEvent;
        var tiltX = 0;
        var tiltY = 0;
        var raf;
        var mouseX = 0;
        var mouseY = 0;
        var paused = false;
        var options = option

        function init() {
            if (!canvasSupport) {
                return;
            }
            canvas = document.createElement('canvas')
            el.appendChild(canvas)
            ctx = canvas.getContext('2d');
            styleCanvas();
            var numParticles = Math.round((canvas.width * canvas.height) / options.density);
            for (var i = 0; i < numParticles; i++) {
                var p = new Particle();
                p.setStackPos(i);
                particles.push(p);
            }

            window.addEventListener("resize", throttle(function () {
                resizeHandler();
            }, 20));

            if (options.parallax) {
                document.addEventListener('mousemove',function (e) {
                    mouseX = e.pageX;
                    mouseY = e.pageY;
                })
            }
            if (options.clickAddDot) {
                document.addEventListener('click', function (e) {
                    for (var i = 0; i < options.addDotNumber; ++i) {
                        var p = new Particle();
                        p.position.x = e.pageX;
                        p.position.y = e.pageY;
                        p.setStackPos(particles.length);
                        particles.push(p);
                    }
                });
            }
            if (orientationSupport && !desktop) {
                window.addEventListener('deviceorientation', function () {
                    // Contrain tilt range to [-30,30]
                    tiltY = Math.min(Math.max(-event.beta, -30), 30);
                    tiltX = Math.min(Math.max(-event.gamma, -30), 30);
                }, true);
            }

            draw();
            hook('onInit');
        }

        function start() {
            paused = false;
            draw();
        }

        function pause() {
            paused = true;
        }

        function destroy() {
            el.removeChild(el.firstElementChild);
        }

        function styleCanvas() {
            canvas.width = el.offsetWidth;
            canvas.height = el.offsetHeight;
            var rgb = getRgb(options.dotColor);
            ctx.fillStyle = "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + options.dotOpacity + ")";
            rgb = getRgb(options.lineColor);
            ctx.strokeStyle = "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + options.lineOpacity + ")";
            ctx.lineWidth = options.lineWidth;
        }

        function draw() {
            if (!canvasSupport) {
                return;
            }
            winW = window.innerWidth;
            winH = window.innerHeight;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            for (var i = 0; i < particles.length; i++) {
                particles[i].updatePosition();
            }
            for (var i = 0; i < particles.length; i++) {
                particles[i].draw();
            }
            if (!paused) {
                clearTimeout(raf)
                raf = requestAnimationFrame(draw);
            }
        }

        function resizeHandler() {
            styleCanvas();
            for (var i = particles.length - 1; i >= 0; i--) {
                if (particles[i].position.x > el.offsetWidth || particles[i].position.y > el.offsetHeight) {
                    particles.splice(i, 1);
                }
            }
            var numParticles = Math.round((canvas.width * canvas.height) / options.density);
            if (numParticles > particles.length) {
                while (numParticles > particles.length) {
                    var p = new Particle();
                    particles.push(p);
                }
            } else if (numParticles < particles.length) {
                particles.splice(numParticles);
            }
            for (i = particles.length - 1; i >= 0; i--) {
                particles[i].setStackPos(i);
            }
        }

        function hook(hookName) {
            if (options[hookName] !== undefined) {
                options[hookName].call(el);
            }
        }

        function drawShape(ctx, x, y, i, sideNumber, s) {
            var r = sideNumber * s, o = sideNumber / s, c = 180 * (o - 2) / o, l = Math.PI - Math.PI * c / 180;
            ctx.save();
            ctx.beginPath();
            ctx.translate(x, y);
            ctx.moveTo(0, 0);
            for (var d = 0; d < r; d++) {
                ctx.lineTo(i, 0);
                ctx.translate(i, 0);
                ctx.rotate(l);
            }
            ctx.fill();
            ctx.restore();
        }

        function Particle() {
            if (options.dotMinSize == options.dotMaxSize)
                this.radius = options.dotMinSize;
            else
                this.radius = Math.random() * (options.dotMaxSize - options.dotMinSize) + options.dotMinSize;
            this.stackPos = 0;
            this.active = true;
            this.layer = Math.ceil(Math.random() * 3);
            this.parallaxOffsetX = 0;
            this.parallaxOffsetY = 0;
            // Initial particle position
            this.position = {
                x: Math.ceil(Math.random() * canvas.width),
                y: Math.ceil(Math.random() * canvas.height)
            };
            // Random particle speed, within min and max values
            this.speed = {};
            switch (options.directionX) {
                case 'left':
                    this.speed.x = +(-options.maxSpeedX + (Math.random() * options.maxSpeedX) - options.minSpeedX).toFixed(2);
                    break;
                case 'right':
                    this.speed.x = +((Math.random() * options.maxSpeedX) + options.minSpeedX).toFixed(2);
                    break;
                default:
                    this.speed.x = +((-options.maxSpeedX / 2) + (Math.random() * options.maxSpeedX)).toFixed(2);
                    this.speed.x += this.speed.x > 0 ? options.minSpeedX : -options.minSpeedX;
                    break;
            }
            switch (options.directionY) {
                case 'up':
                    this.speed.y = +(-options.maxSpeedY + (Math.random() * options.maxSpeedY) - options.minSpeedY).toFixed(2);
                    break;
                case 'down':
                    this.speed.y = +((Math.random() * options.maxSpeedY) + options.minSpeedY).toFixed(2);
                    break;
                default:
                    this.speed.y = +((-options.maxSpeedY / 2) + (Math.random() * options.maxSpeedY)).toFixed(2);
                    this.speed.x += this.speed.y > 0 ? options.minSpeedY : -options.minSpeedY;
                    break;
            }
        }

        Particle.prototype.setStackPos = function (i) {
            this.stackPos = i;
        };

        Particle.prototype.updatePosition = function () {
            if (options.parallax) {
                if (orientationSupport && !desktop) {
                    // Map tiltX range [-30,30] to range [0,winW]
                    var ratioX = (winW - 0) / (30 - -30);
                    pointerX = (tiltX - -30) * ratioX + 0;
                    // Map tiltY range [-30,30] to range [0,winH]
                    var ratioY = (winH - 0) / (30 - -30);
                    pointerY = (tiltY - -30) * ratioY + 0;
                } else {
                    pointerX = mouseX;
                    pointerY = mouseY;
                }
                this.parallaxTargX = (pointerX - (winW / 2)) / (options.parallaxMultiplier * this.layer);
                this.parallaxOffsetX += (this.parallaxTargX - this.parallaxOffsetX) / 10; // Easing equation
                this.parallaxTargY = (pointerY - (winH / 2)) / (options.parallaxMultiplier * this.layer);
                this.parallaxOffsetY += (this.parallaxTargY - this.parallaxOffsetY) / 10; // Easing equation
            }
            switch (options.directionX) {
                case 'left':
                    if (this.position.x + this.speed.x + this.parallaxOffsetX < 0) {
                        this.position.x = el.offsetWidth - this.parallaxOffsetX;
                    }
                    break;
                case 'right':
                    if (this.position.x + this.speed.x + this.parallaxOffsetX > el.offsetWidth) {
                        this.position.x = 0 - this.parallaxOffsetX;
                    }
                    break;
                default:
                    // If particle has reached edge of canvas, reverse its direction
                    if (this.position.x + this.speed.x + this.parallaxOffsetX > el.offsetWidth || this.position.x + this.speed.x + this.parallaxOffsetX < 0) {
                        this.speed.x = -this.speed.x;
                    }
                    break;
            }
            switch (options.directionY) {
                case 'up':
                    if (this.position.y + this.speed.y + this.parallaxOffsetY < 0) {
                        this.position.y = el.offsetHeight - this.parallaxOffsetY;
                    }
                    break;
                case 'down':
                    if (this.position.y + this.speed.y + this.parallaxOffsetY > el.offsetHeight) {
                        this.position.y = 0 - this.parallaxOffsetY;
                    }
                    break;
                default:
                    // If particle has reached edge of canvas, reverse its direction
                    if (this.position.y + this.speed.y + this.parallaxOffsetY > el.offsetHeight || this.position.y + this.speed.y + this.parallaxOffsetY < 0) {
                        this.speed.y = -this.speed.y;
                    }
                    break;
            }
            // Move particle
            this.position.x += this.speed.x;
            this.position.y += this.speed.y;
        };

        Particle.prototype.draw = function () {
            switch (options.dotShape) {
                case "circle":
                    ctx.beginPath();
                    ctx.arc(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY, this.radius, 0, Math.PI * 2, true);
                    ctx.closePath();
                    ctx.fill();
                    break;
                case "edge":
                    ctx.beginPath();
                    ctx.rect(this.position.x + this.parallaxOffsetX - this.radius, this.position.y + this.parallaxOffsetY - this.radius, 2 * this.radius, 2 * this.radius);
                    ctx.closePath();
                    ctx.fill();
                    break;
                case "triangle":
                    drawShape(ctx, this.position.x + this.parallaxOffsetX - this.radius, this.position.y + this.parallaxOffsetY + this.radius / 1.66, 2 * this.radius, 3, 2);
                    break;
                case "polygon":
                    drawShape(ctx, this.position.x + this.parallaxOffsetX - this.radius / (options.sideNumber / 3.5), this.position.y + this.parallaxOffsetY - this.radius / .76, 2.66 * this.radius / (options.sideNumber / 3), options.sideNumber, 1);
                    break;
                case "star":
                    drawShape(ctx, this.position.x + this.parallaxOffsetX - 2 * this.radius / (options.sideNumber / 4), this.position.y + this.parallaxOffsetY - this.radius / 1.52, 2 * this.radius * 2.66 / (options.sideNumber / 3), options.sideNumber, 2);
                    break;
                case "heart":
                    ctx.save();
                    ctx.beginPath();
                    ctx.translate(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY);
                    ctx.rotate(Math.PI / 4);
                    ctx.fillRect(-this.radius, -this.radius, 2 * this.radius, 2 * this.radius);
                    ctx.beginPath();
                    ctx.arc(-this.radius, 0, this.radius, Math.PI / 2, 3 * Math.PI / 2);
                    ctx.fill();
                    ctx.beginPath();
                    ctx.arc(0, -this.radius, this.radius, Math.PI, 2 * Math.PI);
                    ctx.fill();
                    ctx.restore();
                    break;
                default:
                    //ctx.arc(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY, this.radius, 0, Math.PI * 2, true);
                    break;
            }
            // Draw lines
            ctx.beginPath();
            // Iterate over all particles which are higher in the stack than this one
            for (var i = particles.length - 1; i > this.stackPos; i--) {
                var p2 = particles[i];
                // Pythagorus theorum to get distance between two points
                var a = this.position.x - p2.position.x;
                var b = this.position.y - p2.position.y;
                var dist = Math.sqrt((a * a) + (b * b)).toFixed(2);
                // If the two particles are in proximity, join them
                if (dist < options.proximity) {
                    //var opacity = options.lineOpacity - dist / (1 / options.lineOpacity) / options.proximity;
                    //opacity = opacity >= 0 ? opacity : 0;
                    //ctx.strokeStyle = "rgba(255,255,255,"+ opacity +")";
                    ctx.moveTo(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY);
                    if (options.curvedLines) {
                        ctx.quadraticCurveTo(Math.max(p2.position.x, p2.position.x), Math.min(p2.position.y, p2.position.y), p2.position.x + p2.parallaxOffsetX, p2.position.y + p2.parallaxOffsetY);
                    } else {
                        ctx.lineTo(p2.position.x + p2.parallaxOffsetX, p2.position.y + p2.parallaxOffsetY);
                    }
                }
            }
            ctx.stroke();
            ctx.closePath();
        };

        init();

        return {
            options: option,
            destroy: destroy,
            start: start,
            pause: pause
        };
    }

